{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 简介\n",
    "在机器学习领域，集成学习是用于提高模型的准确性、稳健性和泛化的最强大技术之一。集成学习不依赖于耽搁预测模型，而是将多个模型的预测相结合，以创建更准确，更可靠的最终预测。直觉是多个模型或弱学习器可以纠正彼此的错误，从而产生更健壮的学习器。\n",
    "主要的集成方式有: Bagging,Boosting,Stacking,Voting,Blending 等。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 集成学习的优点\n",
    "- 提高准确性: 通过平均或组多个模型的预测，集成通用优于单个模型。\n",
    "- 减少过拟合：集成方法通过平滑噪声预测来帮助减少过拟合。\n",
    "- 模型多样性：集成使用多种算法或统一算法的变体，可以捕获数据的不同方面。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型的偏差和方差\n",
    "\n",
    "偏差：描述样本拟合出的模型的预测结果的期望与样本真实结果的差距，要想偏差表现的好，就需要复杂化模型，增加模型的参数，但这样容易过拟合，过拟合对应上图的 High Variance，点会很分散。低偏差对应的点都打在靶心附近，所以喵的很准，但不一定很稳；\n",
    "方差：描述样本上训练出来的模型在测试集上的表现，要想方差表现的好，需要简化模型，减少模型的复杂度，但这样容易欠拟合，欠拟合对应上图 High Bias，点偏离中心。低方差对应就是点都打的很集中，但不一定是靶心附近，手很稳，但不一定瞄的准。\n",
    "\n",
    "![image.png](images/bias_variance.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Bagging（Bootstrap Aggregation）\n",
    "\n",
    "Bagging 是一种集成技术，旨在通过独立训练多个模型，然后**平均**它们的预测来减少方差并防止过度拟合。Bagging 的工作原理是通过引导生成不同的训练数据集，并在每个数据集上训练单独的模型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Bagging原理\n",
    "\n",
    "Bagging 是一种集成技术，旨在通过独立训练多个模型，然后**平均**它们的预测来减少方差并防止过度拟合。Bagging 的工作原理是**通过引导生成不同的训练数据集，并在每个数据集上训练单独的模型**。\n",
    "\n",
    "1. Bootstrap Sampling：从训练集中随机采样数据并进行替换以创建多个子集（Bootstrap 样本）。某些数据点可能会重复，而其他数据点可能会在每个样本中省略。\n",
    "2. 训练模型 ：在每个引导样本上训练一个单独的模型（通常是弱学习器，如决策树）。\n",
    "3. 聚合预测 ：对于分类任务，使用多数投票合并来自所有模型的预测。对于回归任务，模型的预测是平均的。\n",
    "\n",
    "![image.png](images/bagging_workflow.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 生成训练数据集的步骤：\n",
    "\n",
    "1. 原始数据集：从大小为 N 的原始训练数据集开始，包含 N 个数据点或样本。\n",
    "2. Sampling with Replace: 对于每个 boostrap数据集，从原始数据集中**随机选择 N 个样本**。由于这是带替换的采样，因此在选择样本后，他会返回到数据集，因此将来可以再次选择它。\n",
    "   * 某些样本将会被多次选择\n",
    "   * 在特定 Boostrap 数据集中可能根本没有选择其他样本。\n",
    "3. 创建多个 Boostrap 数据集：多次重复上述过程生成 M 个不同的 Boostrap 数据集，其中 M 是您计划在 bagging 集成中训练的模型(或弱学习器)的数量。这些数据集中的每一个都将具有相同的样本数(N),但由于随机采样，每个数据集中的数据点分布会有所不同。\n",
    "4. 训练模型:在每个引导的数据集上训练一个单独的模型(例如:决策树)。\n",
    "5. 聚合预测: 训练完成所有模型后，将他们的预测(例如，在分类人物中通过多数投票或在回归人物中平均)进行最终预测。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Boostrapping 引导\n",
    "在引导过程中，我们可以从数据集的行和列中采样，具体取决于所使用的集成方法。以下是它的工作原理：\n",
    "\n",
    "1. 采样行：标准 Boostrapping\n",
    "    * 在经典的 boostrapping 中（就像在 Bagging 中一样),我们使用 Replacement 随机采样行以创建数据集的子集。\n",
    "    * 每个子集可能具有重复的行，并且通常与原始数据集的大小相同。 采样列：特征子集 \n",
    "3. 采样列：特征子集\n",
    "    * 在某些方法（如随机森林）中，我们还可以对特征（列）的子集进行采样，以便在模型中使用。\n",
    "    * 这样做通常是为了引入额外的随机性并减少过拟合的机会。\n",
    "\n",
    "**我们可以同时对rows 和 columns进行采样吗?**\n",
    "是的！对 rows 和 columns 进行采样是可能的，有时以 ensemble 方法完成。下面是一个示例：\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### bagging 举例\n",
    "\n",
    "#### 数据集\n",
    "具有 5 条记录（行）和 5 个特征（列）的数据集：\n",
    "排列:R1,R2,R3,R4,R5,特征:F1,F2,F3,F4,F5\n",
    "#### 场景\n",
    "对 3 行和 3 个特征进行采样以训练单个模型:\n",
    "* 采样行:R2,R3,R5\n",
    "* 采样特征:F1,F3,F4\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Bagging的优缺点\n",
    "\n",
    "#### 优点\n",
    "* 减少方差 ：通过对多个模型的预测进行平均，装袋可以减少模型的方差，使其不太可能过度拟合。\n",
    "* 可并行化 ：由于每个模型都是独立训练的，因此可以轻松并行化 bagging。\n",
    "\n",
    "#### 缺点\n",
    "* 对偏差效果较差 ：装袋主要减少方差，但如果单个模型较弱，则不会显著减少偏差。\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bias 在 Bagging 背景下的意思\n",
    "当我们说 bagging 减少方差但不会显著减少偏差时，我们强调了模型可能存在的两种错误之间的区别：\n",
    "\n",
    "1. 偏差（系统错误）:\n",
    "   - 这是由于模型中的假设过于简单而导致的错误。如果单个模型（例如，决策树）很弱且过于简单，它们将无法捕获数据的全部复杂性。因此，即使在 bagging 之后，集成模型可能仍然具有高偏差，这意味着它会系统性地做出错误的预测。\n",
    "   - 例如，如果基础模型是一个修剪过多的决策树（浅树），它可能无法捕获特征之间的重要交互，从而导致即使在训练数据上也会导致预测不准确。\n",
    "\n",
    "2. 方差（对数据的敏感性）:\n",
    "   - 方差是指模型对训练数据波动的敏感度。具有高方差的模型（例如深度决策树）可能会过度拟合训练数据，从而捕获噪声并导致对看不见的数据的泛化效果不佳。Bagging 通过组合在不同数据子集上训练的多个模型，有助于消除这种过度拟合，从而减少方差。\n",
    "   - 一个好的模型应该具有低方差（对异常值不太敏感）， 因为高方差会导致过度拟合，这意味着该模型在训练数据上表现良好，但在看不见的数据上表现不佳（即，它无法泛化）。\n",
    "\n",
    "在 bagging 的上下文中， 弱学习器是具有高偏差但低方差的模型。例如，浅层决策树可能表现不佳，因为它没有以足够详细的方式对数据进行建模（高偏差）。当 bagging 组合了许多这些弱学习器时，由于模型是聚合的，因此集成的方差会减小，但由于所有单个模型都存在系统性偏差，因此 bagging 不会纠正这种偏差。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Boosting\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 概述\n",
    "\n",
    "Boosting 是另一种集成技术，它侧重于通过按顺序训练模型来减少偏差和方差 ，其中每个后续模型都尝试纠正前一个模型的错误。与 bagging 不同，在 bagging 中，模型是独立训练的，而 Boosting 是迭代构建模型的。在回归的情况下，测试数据将转到每个模型，并给我们每个模型返回的所有值的平均值。对于分类，将进行投票（技术术语 — 最大投票分类器），并且具有多数结果的类将被宣布为最终结果"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Boosting 步骤\n",
    "\n",
    "1. 按顺序训练弱学习者 ：模型一个接一个地训练，每个模型都更加关注之前模型错误分类的数据点。\n",
    "2. 调整权重 ：在每次迭代中，错误预测样本的权重都会增加，因此后续模型会更多地关注那些难以预测的样本。\n",
    "3. 聚合预测 ：最终预测是通过取各个模型预测的加权平均值或总和进行的。\n",
    "\n",
    "![image.png](images/boosting_workflow.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Boosting 优缺点\n",
    "\n",
    "#### 优点\n",
    "- 减少偏差和方差 ：提升可减少模型的偏差和方差，从而获得高度准确的预测。\n",
    "- 适用于较弱的学习器 ：即使是较弱的模型（如浅层决策树），也可以通过提升进行组合，以创建强大的预测器。\n",
    "\n",
    "#### 缺点\n",
    "- 对异常值敏感 ：由于 boosting 侧重于纠正错误，因此它可能会过于强调干扰数据点或异常值。\n",
    "- 计算成本高昂：提升是一个连续过程，因此比 bagging 等可并行化方法慢。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Stacking 堆叠\n",
    " Stacking 是一种复杂的集成技术，设计组合不同类型的模型(通常成为基本学习器) 以提高性能。堆叠背后的想法是通过训练元模型(通常成为二级模型)来利用多个模型的优势，该元模型学习根据基本模型的输出进行预测。\\\n",
    "\n",
    " ![image.png](images/stacking_intro.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stacking工作原理\n",
    "1. 在训练数据上训练多个基础模型(例如，决策树、逻辑回归、SVM)。\n",
    "2. 这些基础模型的预测被馈送到元模型(通常是更复杂的模型，如神经网络或线性回归）中。\n",
    "3. 元模型学习组合基础模型的预测并输出最终预测。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stacking 步骤\n",
    "\n",
    "1. 有 Train Data 和 Test Data。假设我们使用 4 被交叉严重来训练基础模型，则 Train Data 分为 4 部分。\n",
    "\n",
    "![image.png](images/stacking_step_1.png)\n",
    "\n",
    "2. 使用 4 部分 Train Data，将第 1个基础模型(假设他是一个决策树)拟合到3 个部分，并对第 4 部分进行预测。这是针对训练数据的每个部分完成的。最后，训练数据中的所有实例都将具有预测。这将 Train Data 创建一个新特征，将其成为 pred_m1 (预测模型 1)。\n",
    "\n",
    "![image.png](images/stacking_step_2.png)\n",
    "\n",
    "3. 然后将模型 1（决策树）拟合到整个训练数据上-这次不需要折叠。经过训练的模型将用于预测测试数据。所以 test data 也将有 pred_m1。\n",
    "\n",
    "![image.png](images/stacking_step_3.png)\n",
    "\n",
    "4. 对第 2 个模型（例如 KNN)和第 3 个模型（例如 SVM)重复第 2 至3 步。这些将 Train Data 和 Test Data 预测中的另外两个特征，即 pred_m2 和 pred_m3。\n",
    "\n",
    "![image.png](images/stacking_step_4.png)\n",
    "\n",
    "5. 现在，要训练元模型 (假设它是一个逻辑回归)，我们只使用基础模型中心添加的特征，即[pred_m1, pred_m2, pred_m3]。在 train data 上拟合此元模型。\n",
    "   \n",
    "   ![image.png](images/stacking_step_5.png)\n",
    "\n",
    "6. test_data 的最终预测由经过训练的元模型给出。\n",
    "\n",
    "在分类问题中，您可以训练三个模型：决策树、SVM 和 k 最近邻模型。然后，这些模型的输出用作元模型（例如，逻辑回归）的特征，从而做出最终的分类决策。\n",
    "\n",
    "堆叠的优点:\n",
    "* 组合具有不同强度的模型以提高整体性能。\n",
    "* 通过比使用任何单个模型获得更好的性能。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Voting（投票）\n",
    "在投票中，多个模型在同一数据集上独立训练，在分类任务的情况下通过投票来组合它们的预测，在回归任务的情况下通过平均来组合它们的预测。这是最简单的集成方法之一，可以分为两种类型： 硬投票和软投票 。\n",
    "- Hard Voting: 在分类任务中，最终的集成预测是通过选择从基本模型预测中获得最多票数的类来确定。这通常被称为“硬投票”。\n",
    "- Soft Voting: 在回归任务中，最终预测通常是通过对基本模型的预测求平均值来获得的。这也称为“软投票”。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Voting 的优点\n",
    "* 易于实施和解释\n",
    "* 可以通过组合不同的模型来提高准确性。\n",
    "* 当基本模型相当强大且互补时，效果很高。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bagging和Voting的却别\n",
    "|Aspect|Bagging|Voting|\n",
    "|------|-------|------|\n",
    "|DataSet Subset|模型用不同的子集训练|模型用相同的数据集训练|\n",
    "|Model Diversity|一般用相同的模型(如决策树)｜通常用不同的模型(tree,svm,logistic regression)|\n",
    "|Training Process|用 boostrapping 生成不同维度数据|不是用 boostraping,模型使用相同的数据|\n",
    "|Aggregation Method|在最终预测中使用多数投票（分类）或平均（回归）的方法。|使用多数表决法（用于分类任务）或求平均值的方法（用于回归任务）。|\n",
    "|Purpose|Primarily aims to reduce variance and overfitting by averaging models trained on different datasets|aims to leverage the strengths of different models to improve gernalization|\n",
    "|Examples|Random Forest(mulitple decision tress)|Combining SVM,decision tree,and logistic regression|"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Blending（混合）\n",
    "混合与堆叠非常相似。它还使用基础模型将基础预测作为新特征提供，并在给出最终预测的新功能上训练新的元模型。 唯一的区别是元模型的训练应用于单独的维持集（例如 train_data 的 10%），而不是应用于完整和折叠的训练集。\n",
    "混合和堆叠之间的唯一区别是，混合使用维持集的概念来测试模型的性能，而堆叠使用 k 折叠交叉验证的概念来检查模型的性能。然后，从这些模型获得的结果将被发送到元模型，其中输入特征是每个模型做出的预测，输出将是与模型预测的数据相对应的实际值\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Blending于Stacking 的区别\n",
    "混合和堆叠之间的唯一区别是，混合使用维持集的概念来测试模型的性能，而堆叠使用 k 折叠交叉验证的概念来检查模型的性能。然后，从这些模型获得的结果将被发送到元模型，其中输入特征是每个模型做出的预测，输出将是与模型预测的数据相对应的实际值\n",
    "\n",
    "### 堆叠的主要特征:\n",
    "- 训练数据 ：将原始数据集划分为 k 折以进行交叉验证：\n",
    "- 每个基本模型都在 k-1 个折叠上进行训练，并对剩余的折叠进行预测。此过程重复 k 次，每个折叠用作验证集一次。\n",
    "- 收集来自每个基础模型在所有折叠中的预测，并用于训练元模型。\n",
    "- 元模型 ：根据所有折叠的基础模型的预测进行训练。此元模型学习组合基础模型的预测以进行最终预测。\n",
    "\n",
    "![image.png](images/blending_different_stacking.png)\n",
    "\n",
    "### 混合主要特征\n",
    "- 训练数据 ：原始数据集分为两部分：\n",
    "- 训练集 ：用于训练基础模型。\n",
    "- Holdout Set： 用于从基本模型生成预测，然后使用这些预测来训练元模型。\n",
    "- 元模型 ： 元模型根据基础模型在维持集上所做的预测进行训练。此元模型学习组合基础模型的预测以进行最终预测。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Blending 步骤\n",
    "\n",
    "第 1 步 ：train_data 分为 base_train_data 和 holdout_set。\n",
    "\n",
    "![image.png](images/blending_step_1.png)\n",
    "\n",
    "第 2 步 ：在 base_train_data 上拟合基本模型，并根据 holdout_set 和 test_data 进行预测。这些将创建新的预测特征。\n",
    "\n",
    "![image.png](images/blending_step_2.png)\n",
    "\n",
    "第 3 步 ：然后将新的元模型拟合到具有新预测特征 holdout_set。将使用 holdout_set 的原始和元功能。\n",
    "\n",
    "第 4 步 ：经过训练的元模型用于使用原始和新的元特征对测试数据进行最终预测。\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 算法应用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## GBDT(DT+Boosting)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 随机森林(DT+Bagging)\n",
    "\n",
    "GBDT（Gradient Boosting Decision Tree）是一种迭代的决策树算法，该算法由多棵决策树组成，从名字中我们可以看出来它是属于 Boosting 策略。GBDT 是被公认的泛化能力较强的算法。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 回归树（Regression Decision Tree）\n",
    "\n",
    "如果认为 GBDT 由很多分类树那就大错特错了（虽然调整后也可以分类）。对于分类树而言，其值加减无意义（如性别），而对于回归树而言，其值加减才是有意义的（如说年龄）。GBDT 的核心在于累加所有树的结果作为最终结果，所以 GBDT 中的树都是回归树，不是分类树，这一点相当重要。\n",
    "回归树在分枝时会穷举每一个特征的每个阈值以找到最好的分割点，衡量标准是最小化均方误差。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  梯度迭代（Gradient Boosting）\n",
    "\n",
    "    上面说到 GBDT 的核心在于累加所有树的结果作为最终结果，GBDT 的每一棵树都是以之前树得到的残差来更新目标值，这样每一棵树的值加起来即为 GBDT 的预测值。\n",
    "模型的预测值可以表示为：\n",
    "$$ F_k(x) = \\sum_{i=1}^{k} f_i(x) $$\n",
    "\n",
    "$f_i(x)$ 表示模型与其它权重的成绩，模型的训练目标是使预测值$F_k(x)$逼近真实值$y$, 也就是说要让每个基础模型的预测值逼近各自要预测的部分的真实值。由于要同时考虑所有基模型，导致了整体模型的训练变成了一个非常复杂的问题。所以研究这门想到了一个贪心的解决手段：每次值训练一个基模型。那么，现在改写整体模型迭代为:\n",
    "$$ F_k(x) = F_{k-1}(x)+f_k(x)$$\n",
    "这样一来，每一轮迭代中，只要集中解决一个基础模型的训练问题:使$f_k(x)$逼近真实值$y$。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 举例说明:\n",
    "举个例子：比如说 A 用户年龄 20 岁，第一棵树预测 12 岁，那么残差就是 8，第二棵树用 8 来学习，假设其预测为 5，那么其残差即为 3，如此继续学习即可。\n",
    "那么 Gradient 如何提现呢? 其实很简单，**残差其实就是最小均方损失函数关于预测值的反向梯度(划重点)**\n",
    "$$ -\\frac{\\partial (\\frac{1}{2} (y-F_k(x))^2)}{\\partial F_k(x)} = y-F_k(x) $$\n",
    "也就是说，预测值和实际值的残差与损失函数的负梯度相同。\n",
    "但要注意，基于残差 GBDT 容易对异常值敏感，举例：\n",
    "\n",
    "| 函数  | 表头2 | 表头3 |表头4 | 表头5｜\n",
    "| :---- | ----: | :---: | :----: | :----: |\n",
    "|$y_i$|0.5 | 1.2 | 2 | $5^\\star$ |\n",
    "|$F(x_i)$|0.6|1.4|1.5|1.7|\n",
    "|$L=\\frac{(y-F)^2}{2}$|0.005|0.02|0.125|5.445|\n",
    "\n",
    "很明显后续的模型会对第 4 个值关注过多，这不是一种好的现象，所以一般回归类的损失函数会用绝对损失或者 Huber 损失函数来代替平方损失函数。\n",
    "\n",
    "- Absolute Loss(more robust to outliers):\n",
    "$$ L = |y-F| $$\n",
    "- Huber Loss(more robust to outliers):\n",
    "$$ L = \\begin{cases} \\frac{1}{2}(y-F)^2, &  |y-F| \\leq \\delta \\\\ \\delta(|y-F|-\\frac{d}{2}), & |y-F|>\\delta \\end{cases} $$\n",
    "\n",
    "\n",
    "| 函数  | 表头2 | 表头3 |表头4 | 表头5｜\n",
    "| :---- | ----: | :---: | :----: | :----: |\n",
    "|$y_i$|0.5|1.2|2|$5^\\star$|\n",
    "|$F(x_i)$|0.6|1.4|1.5|1.7|\n",
    "|Square Loss|0.005|0.02|0.125|5.445|\n",
    "|Absolute Loss|0.1|0.2|0.5|3.3|\n",
    "|Huber Loss($\\delta = 0.5$)|0.005|0.02|0.125|1.525|\n",
    "\n",
    "GBDT 的 Boosting 不同于 Adaboost 的 Boosting，GBDT 的每一步残差计算其实变相地增大了被分错样本的权重，而对与分对样本的权重趋于 0，这样后面的树就能专注于那些被分错的样本。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 缩减（Shrinkage）\n",
    "\n",
    "Shrinkage 的思想认为，每走一小步逐渐逼近结果的效果要比每次迈一大步很快逼近结果的方式更容易避免过拟合。即它并不是完全信任每一棵残差树。\n",
    "\n",
    "$$ F_i(x) = F_{i-1}(x)+\\mu f_i(x) , (0< \\mu \\leq 1 )\n",
    "\n",
    "Shrinkage 不直接用残差修复误差，而是只修复一点点，把大步切成小步。本质上 Shrinkage 为每棵树设置了一个 weight，累加时要乘以这个 weight，当 weight 降低时，基模型数会配合增大。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 优缺点\n",
    "\n",
    "优点:\n",
    "- 可以自动进行特征组合，拟合非线性数据；\n",
    "- 可以灵活处理各种类型的数据。\n",
    "\n",
    "缺点:\n",
    "- 对异常点敏感。\n"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
